#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>

#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */

#include "GenericServer.h"
#include "tree.h"
#include "List.h"
#include "STScheduler.h"

#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define BACKLOG (10)
#define PQ_SIZE (20)
#define MAX_SIZE (200)


struct GenServer_t
{
    int             m_tcpFd        ;       /*  File descriptor of the listen socket        */
    int             m_udpFd           ;       /*  File descriptor of the UDP socket           */
    int             m_port            ;       /*  File descriptor of the listen socket        */
    int             m_serverStop      ;       /*  Flag for stopping the running select loop   */
    void*           m_application     ;       /*  Pointer for application                     */
    size_t          m_headerSize      ;       /*  Header size in bytes                        */
    HeaderCalcFunc  m_headCalcFunc    ;
    NewClientFunc   m_newClientFunc   ;
    NewMssgFunc     m_newMssgFunc     ;
    DisconnectFunc  m_disconnectFunc  ;
    Tree*           m_clientsTree     ;       /*  Tree of clients data                        */
    Tree*           m_udpClientsTree  ;
    Scheduler*      m_scheduler       ;
};

typedef struct IORequest_t
{
    char*   m_buf;              /*  The buffer to read to it or write from it           */
    size_t  m_size;             /*  size of the request for write or read               */
    size_t  m_bytesDone;        /*  bytes that already sent or bytes already written    */
}IORequest;


typedef enum RecvState_t
{
    READ_HEADER,
    READ_DATA
}RecvState;

typedef struct RecvData_t
{
    char*       m_headBuf       ;
    char*       m_buf           ;        /*  The buffer to read to it or write from it           */
    size_t      m_size          ;        /*  size of the request for write or read               */
    size_t      m_bytesDone     ;        /*  bytes that already sent or bytes already written    */
    RecvState   m_recvState     ;
}RecvData;

typedef enum DropState_t
{
    DS_NON_DROPPED      = 0,
    DS_SERVER_DROPPED   = 1,
    DS_SELF_DROPPED     = 2
}DropState;



typedef struct ClientUdpData_t
{
    struct sockaddr_in  m_clientAddr  ;
    DropState           m_drop        ;   /*  drop flag state: 1. connected (non-dropped). 2. dropped by server.  3. disconnected   */
    List*               m_sendList    ;   /*  IORequest list for send */
}ClientUdpData;


typedef struct ClientData_t
{
    int         m_fd          ;
    DropState   m_drop        ;   /*  drop flag state: 1. connected (non-dropped). 2. dropped by server.  3. disconnected   */
    List*       m_sendList    ;   /*  IORequest list for send */
    RecvData    m_recvRq      ;   /*  IORequest of read       */
    IORequest   m_excpRq      ;   /*  IORequest of excp       */
}ClientData;

/****************************************************************************************************************************************/
/********************************          Internal Functions Forward Decleration        ************************************************/
/****************************************************************************************************************************************/
/*  Function for creating new client data and add the client into a client's container. Also call the user call back for new client.    *
 *  On succes return 0. On failure of allocation or creating send list return error numbers and prints to stderr                        */
static int AddClient(GenServer* _server, int _clientFd);

/*  Less function for clientsTree   */
static int LessClientFd(void* _cl1, void* _cl2);

/*  Function to init the server connection. Create socket, bind, listen and saves the socket fd for new connections in server's struct. *
 *  On failure return error numbers and prints to stderr. Failure can occure from system call failures                                  */
static int Init(GenServer* _server);

/*  Accept new client connection. The function call to accept system call to get client fd, and call to AddClient function.             *
 *  On failure return error numbers and prints to stderr, error can occure on system call failure or AddClient failed                   */
static int Accept(GenServer* _server);

/*  Init the fd sets for the select loop. The actual remove of a client will be done here, if there isn't write mission.                *
 *  Return value is the maxFd in sets. Read & Excp will be always set, if not marked toDrop. Write will be set if there send            *
 *  Requests in the sendList.                                                                                                           */
static int InitFdSets(GenServer* _server, fd_set* _readFds, fd_set* _writeFds, fd_set* _excpFds);

/*  Remove client. The function Delete its list and free the allocated fields.                                                          */
static void RemoveClient(GenServer* _server,ClientData* _client);

/*  Internal function for recieve. start to fill buffer from *_totBytesRcv amount of _size. The parameters are: server, client,         *
 *  the buffer to be filled, size to read, OUTPUT param of total bytes recieved and flags of either: OOB, MSG_DONTWAIT or 0.            *
 *  Return 0 if can continue the recv loop, 1 to break loop, -1 if critical error arrived.                                              */
static int InternalRecv(ClientData* _client, char* _buf);

/*  Function for read message recieved from clients. The function read header to header buffer, calculate data size and then            *
 *  read the data to new allocated buffer. Checking the progress of reading and update the read bytes. After total read done            *
 *  calls to callback function NewMssgFunc , and free the buffer. Return 0 on success and errNumber on failure.                         */
static int Recv(GenServer* _server, ClientData* _client);

/*  Function for sending messages to clients. The function take care on send list - Check the progress of writing and update            *
 *  the written bytes, if finished free buffer and pop the request from list.                                                           *
 *  Return 0 on success, 1 when connection shutdown or would block, -1 when critical error occured.                                     */
static int Send(GenServer* _server, ClientData* _client);

/*  Calculate the non written bytes that on send list, after checking request free buffer and pop it from list.                         *
 *  Return the amount of bytes non written.                                                                                             */
static size_t CalcAndFreeNonWritten(ClientData* _client);

static int CreateSocket(GenServer* _server, int _sockType, int* _sockFdPtr);
/****************************************************************************************************************************************/
/*********************************************          Internal Functions         ******************************************************/
/****************************************************************************************************************************************/
static int AddClient(GenServer* _server, int _clientFd)
{
    ClientData* client = 0;
    int         retVal    ;

    assert(_server);
    assert(_clientFd >= 0);

    client = (ClientData*)malloc(sizeof(ClientData));
    if(!client)
    {
        perror("error in allocating client");
        return GSERR_ALLOCATION;
    }

    client->m_fd    = _clientFd     ;
    client->m_drop  = DS_NON_DROPPED;                                               /*  Set drop to 0, until call to ServerDrop => set to 1 */

    /*      Init the recv & excp buffers and data           */
    client->m_recvRq.m_headBuf = (char*)malloc(_server->m_headerSize);              /*  Allocate header buffer in sizeof headerSize         */
    if(!client->m_recvRq.m_headBuf)
    {
        perror("error on allocation");
        retVal = GSERR_ALLOCATION;
        goto cleanHeadBuf;
    }
    client->m_recvRq.m_bytesDone = 0                    ;
    client->m_recvRq.m_buf       = 0                    ;
    client->m_recvRq.m_size      = _server->m_headerSize;
    client->m_excpRq.m_bytesDone = 0                    ;
    client->m_excpRq.m_buf       = 0                    ;
    client->m_recvRq.m_recvState = READ_HEADER;

    client->m_sendList  = ListNew() ;                                               /*  Create new list of send requests                    */
    if(!client->m_sendList)
    {
        fprintf(stderr, "error on creating list\n");
        retVal = GSERR_NEW_LIST;
        goto cleanList;
    }

    /*      Insert client to tree. check the enter succeed, by checking we didn't get end stab      */
    if(TreeIsEqual(TreeInsert(_server->m_clientsTree, client), TreeEnd(_server->m_clientsTree)))
    {
        fprintf(stderr, "error in inserting client to container\n");
        retVal = GSERR_INSERT_TREE;
        goto cleanTreeInsert;
    }
    _server->m_newClientFunc(_server->m_application, client);                    /*  Activate the callback for new client                */

    return 0;


    cleanTreeInsert:                        /*  labels for goto */
        ListDel(client->m_sendList);
    cleanList:
        free(client->m_recvRq.m_headBuf);
    cleanHeadBuf:
        free(client);
        return retVal;
}


static void RemoveClient(GenServer* _server,ClientData* _client)
{
    size_t      bytesRecv    = 0;
    size_t      bytesToWrite    ;

    assert(_server);
    assert(_client);

    bytesToWrite = CalcAndFreeNonWritten(_client);
    if(_client->m_recvRq.m_recvState == READ_HEADER)           /*  Calculate non read                          */
    {
        bytesRecv = _client->m_recvRq.m_bytesDone;
    }
    else                                                        /*  add the header size that already been read  */
    {
        bytesRecv = _client->m_recvRq.m_bytesDone + _server->m_headerSize;
    }

    (*_server->m_disconnectFunc)(_server->m_application, _client, bytesRecv, bytesToWrite);
    ListDel(_client->m_sendList);
    free(_client->m_recvRq.m_headBuf);
    if(_client->m_recvRq.m_buf)                                 /*  Check if the recv buffer is allocated       */
    {
        free(_client->m_recvRq.m_buf);                          /*  Free the buffer of recv                     */
    }
    if(_client->m_excpRq.m_buf)                                 /*  Check if the excp buffer is allocated       */
    {
        free(_client->m_excpRq.m_buf);                          /*  Free the buffer of excp                     */
    }
    if(close(_client->m_fd) == -1)                              /*  close the client fd                         */
    {
        perror("error in closing client fd");
    }

    free(_client);
}


static int LessClientFd(void* _cl1, void* _cl2)
{
    return ((ClientData*)_cl1)->m_fd < ((ClientData*)_cl2)->m_fd;
}


int LessUDPClient(void* _cl1, void* _cl2)
{
    ClientUdpData* cl1 = (ClientUdpData*)_cl1;
    ClientUdpData* cl2 = (ClientUdpData*)_cl2;

    if(cl1->m_clientAddr.sin_port < cl2->m_clientAddr.sin_port)       /*  If ip is smaller return 1                                               */
    {
        return 1;
    }

    if(cl1->m_clientAddr.sin_port == cl2->m_clientAddr.sin_port)      /*  If ip is equal check if the port is smaller and return value according  */
    {
        return cl1->m_clientAddr.sin_addr.s_addr < cl2->m_clientAddr.sin_addr.s_addr;
    }

    return 0;                                                           /*  The second ip is bigger and return 0                                    */
}


static int Init(GenServer* _server)
{
    assert(_server);

    CreateSocket(_server, SOCK_STREAM, &(_server->m_tcpFd));    /*  Create tcp listen socket    */
    CreateSocket(_server, SOCK_DGRAM, &(_server->m_udpFd));     /*  Create udp socket           */

    if(listen(_server->m_tcpFd, BACKLOG))
    {
        perror("listen");
        close(_server->m_tcpFd);
        return GSERR_LISTEN;
    }

    return 0;
}


static int CreateSocket(GenServer* _server, int _sockType, int* _sockFdPtr)
{
    struct sockaddr_in  serverAddr  ;
    int                 optVal = 1  ;

    *_sockFdPtr = socket(AF_INET,                  /*  Specifies the communications domain in which a socket is to be created. AF_INET = internet      */
                        _sockType | SOCK_NONBLOCK,/*  Specifies the type of socket to be created. SOCK_STREAM = TCP ,SOCK_DGRAM = UDP                 */
                        0);                       /*  Specifies a particular protocol to be used with the socket.                                     */
    if(*_sockFdPtr == -1)
    {
        perror("socket");
        return GSERR_SOCKET;
    }

    /* Construct the server sockaddr_in structure */
    memset(&serverAddr, 0, sizeof(serverAddr));             /* Clear struct */
    serverAddr.sin_family = AF_INET;                        /* Internet/IP  */
    serverAddr.sin_port = _server->m_port;                  /* server port  */

    if(_sockType == SOCK_STREAM)           /*  Options relevant to TCP connection.                                                     */
    {
        if (setsockopt(*_sockFdPtr,             /*  socket file descriptor                                                                  */
                       SOL_SOCKET,              /*  the sockets API level, level is specified as SOL_SOCKET                                 */
                       SO_REUSEADDR,            /*  option name - A  TCP local socket address that has been bound is unavailable for some
                                                    time after closing, unless the SO_REUSEADDR flag has been set.                          */
                       &optVal,                 /*  option value - address of binary flag                                                   */
                       sizeof(optVal)) == -1)   /*  option len is a value-result argument, initially containing the size
                                                    of the buffer pointed to by optval, and modified on return to  indicate
                                                    the actual size of the value returned.                                                  */
        {
            perror("setsockopt");
            close(*_sockFdPtr);
            return GSERR_SET_SOCK_OP;
        }
    }

    if(bind(*_sockFdPtr,                        /*  socket file descriptor                                  */
            (struct sockaddr *)&serverAddr,     /*  assigns the address specified to the socket             */
            sizeof(struct sockaddr_in)) == -1)  /*  specifies the size, in bytes, of the address structure  */
    {
        perror("bind");
        close(*_sockFdPtr);
        return GSERR_BIND;
    }

    return 0;
}


static int Accept(GenServer* _server)
{
    int                 connectFd           ;
    int                 retVal              ;
    socklen_t           clientAddrSize      ;
    struct sockaddr_in  clientAddr          ;

    assert(_server);

    clientAddrSize = sizeof(struct sockaddr_in);
    connectFd = accept(_server->m_tcpFd,                 /*  the binding socket fd       */
                       (struct sockaddr *) &clientAddr,     /*  gets the client addr struct */
                       &clientAddrSize);                    /*  size of the excpted struct  */
    if (connectFd == -1)
    {
        perror("accept");
        return GSERR_ACCEPT;
    }
    assert(clientAddrSize == sizeof(struct sockaddr_in));   /*  assert use of inapropriate struct sock_addr */

    retVal = AddClient(_server, connectFd);
    if(retVal)
    {
        close(connectFd);
        return retVal;
    }

    return 0;
}


static int InitTcpFdSets(GenServer* _server, fd_set* _readFds, fd_set* _writeFds, fd_set* _excpFds)
{
    int         maxFd  = 0  ;
    TreeIter    it          ;
    ClientData* client = 0  ;

    it = TreeBegin(_server->m_clientsTree);
    while(!TreeIsEqual(it, TreeEnd(_server->m_clientsTree)))
    {
        client = (ClientData*)TreeGetData(it);
        if(!client->m_drop)                               /*  Check that client not drop-marked                       */
        {
            FD_SET(client->m_fd, _readFds);
            FD_SET(client->m_fd, _excpFds);
        }

        if(!ListIsEmpty(client->m_sendList) && client->m_drop != 2)                /*  Check if there is write request in list                 */
        {
            FD_SET(client->m_fd, _writeFds);
        }
        else if(client->m_drop)                           /*  Check if need to drop: 1. if there isn't write requests & toDrop == 1,  *
                                                           *                         2. toDrop == 2                                   */
        {
            it = TreeRemove(_server->m_clientsTree,it);
            RemoveClient(_server, client);
            continue;
        }
        it = TreeNext(it);
    }

    if(!TreeIsEmpty(_server->m_clientsTree))                /*  Check if there are existing clients on server           */
    {                                                       /*  The max between serverFd vs. max client fd              */
        maxFd = ((ClientData*)TreeGetData(TreePrev(TreeEnd(_server->m_clientsTree))))->m_fd;
    }

    return maxFd;
}

static int InitFdSets(GenServer* _server, fd_set* _readFds, fd_set* _writeFds, fd_set* _excpFds)
{
    int         maxFd  = 0  ;

    assert(_server);
    assert(_readFds);
    assert(_writeFds);
    assert(_excpFds);

    FD_ZERO(_readFds);
    FD_ZERO(_writeFds);
    FD_ZERO(_excpFds);

    FD_SET(0 , _readFds);                        /*  set stdin fd as read => if there an iterfere from stdin.            */
    FD_SET(_server->m_tcpFd, _readFds);          /*  set server fd as read => if there is a new connection accept it.    */
    maxFd = MAX(_server->m_tcpFd, maxFd);
    maxFd = MAX(InitTcpFdSets(_server, _readFds, _writeFds, _excpFds), maxFd);  /*  Init tcp fdsets and check max fd    */

    return maxFd;
}


static int InternalRecv(ClientData* _client, char* _buf)
{
    ssize_t bytesRcv;
    RecvData* recvRq = &(_client->m_recvRq);

    bytesRcv = recv(_client->m_fd, _buf + recvRq->m_bytesDone, recvRq->m_size - recvRq->m_bytesDone, MSG_DONTWAIT);
    if(!bytesRcv || (bytesRcv == -1 && errno == ECONNRESET))                /*  The connection shutdown                                             */
    {
        perror("connection shut down");
        _client->m_drop = DS_SELF_DROPPED;              /*  Connection shutdown by client, 2 == flag for client's disConnection */
        return 1;
    }
    if(bytesRcv == -1)
    {
        if(errno == EINTR)                              /*  A signal interrupted, continue the loop                             */
        {
            return 0;
        }
        if(errno == EWOULDBLOCK) /*  There isn't prepared data to read, need to break loop.              */
        {
            return 1;
        }
        else
        {
            perror("recv");
            return -1;                                  /*  critical error arrived, break loop and return -1.                   */
        }
    }

    recvRq->m_bytesDone += bytesRcv;

    return 0;
}

ClientUdpData* AddUdpClient(GenServer* _server, struct sockaddr_in _clientAddr)
{
    ClientUdpData* client = (ClientUdpData*)malloc(sizeof(ClientUdpData));
    if(!client)
    {
        perror("error on allocating client");
        return 0;
    }

    client->m_clientAddr = _clientAddr;
    client->m_drop = DS_NON_DROPPED;
    if(TreeInsert(_server->m_udpClientsTree, client))
    {
        free(client);
        perror("error: insert client to tree");
        return 0;
    }

    return client;
}


void RemoveUdpClient(GenServer* _server, struct sockaddr_in _clientAddr)
{
    TreeIter it;
    ClientUdpData stab;
    ClientUdpData* client = 0;

    stab.m_clientAddr = _clientAddr;
    it = TreeFind(_server->m_udpClientsTree, &stab);
    it = TreeFind(_server->m_udpClientsTree, &client);
    if(!TreeIsEqual(it, TreeEnd(_server->m_udpClientsTree)))
    {
        client = (ClientUdpData*)TreeGetData(it);
        TreeRemove(_server->m_udpClientsTree, it);
        free(client);
    }
}


int UdpRecv(GenServer* _server)
{
    struct sockaddr_in  clientAddr      ;
    socklen_t           clientAddrSize  ;
    ssize_t             bytesRcv        ;
    size_t              dataSize    = 0 ;
    char*               buf         = 0 ;
    TreeIter            it              ;
    ClientUdpData*      client      = 0 ;
    ClientUdpData       stab            ;

    assert(_server);

    buf = (char*)malloc(MAX_SIZE);
    if(!buf)
    {
        perror("error on allocating buffer");
        return GSERR_ALLOCATION;
    }

    clientAddrSize = sizeof(struct sockaddr_in);
    bytesRcv = recvfrom(_server->m_udpFd, buf, MAX_SIZE, 0, (struct sockaddr *)&clientAddr, &clientAddrSize);
    assert(clientAddrSize == sizeof(struct sockaddr_in));   /*  assert use of inapropriate struct sock_addr */

    if(!bytesRcv || (bytesRcv == -1 && errno == ECONNRESET))                /*  The connection shutdown                                             */
    {
        perror("connection shut down");
        RemoveUdpClient(_server, clientAddr);
        return 0;
    }
    if(bytesRcv == -1 && (errno != EWOULDBLOCK && errno != EINTR))
    {
        perror("recv");
        return GSERR_RECV;                                  /*  critical error arrived on recv                   */
    }

    stab.m_clientAddr = clientAddr;
    it = TreeFind(_server->m_udpClientsTree, &stab);
    if(TreeIsEqual(it, TreeEnd(_server->m_udpClientsTree)))
    {
        client = AddUdpClient(_server, clientAddr);
        if(!client)
        {
            return GSERR_ADD_UDP_CLIENT;
        }
    }
    else
    {
        client = (ClientUdpData*)TreeGetData(it);
    }
    if(bytesRcv >= _server->m_headerSize)
    {
        dataSize = (*_server->m_headCalcFunc)(_server->m_application, buf);
    }
    if(dataSize == bytesRcv - _server->m_headerSize)
    {
        (*_server->m_newMssgFunc)(_server->m_application, (GSKey)client, buf + _server->m_headerSize, dataSize);
    }

    return 0;
}


int CheckUdpSets(GenServer* _server, fd_set* _readFds, fd_set* _writeFds, int* _setFds)
{
    int retVal;

    assert(_server);
    assert(_readFds);
    assert(_writeFds);

    if(FD_ISSET(_server->m_udpFd, _readFds))
    {
        --*_setFds;
        retVal = UdpRecv(_server);
        if(retVal)
        {
            return retVal;
        }
    }

    return 0;
}

static int Recv(GenServer* _server, ClientData* _client)
{
    int        retVal  = 0 ;
    RecvData*  rcvRq   = 0 ;

    assert(_server);
    assert(_server->m_application);
    assert(_client);

    rcvRq = &(_client->m_recvRq);

    while(1)
    {
        if(rcvRq->m_recvState == READ_HEADER)                       /*  read heaer              */
        {
            if(_server->m_headerSize)                               /*  If header size is no 0  */
            {
                retVal = InternalRecv(_client, rcvRq->m_headBuf);
                if(retVal)
                {
                    break;
                }
            }

            if(rcvRq->m_bytesDone == _server->m_headerSize)     /*  Finished reading header */
            {
                rcvRq->m_recvState = READ_DATA;
                rcvRq->m_bytesDone = 0;
                rcvRq->m_size = (*_server->m_headCalcFunc)(_server->m_application, rcvRq->m_headBuf);
            }
        }
        else                            /*  read data   */
        {
            if(rcvRq->m_bytesDone == 0 && !rcvRq->m_buf)    /*  If buffer no allocated  */
            {
                rcvRq->m_buf = malloc(rcvRq->m_size);
                if(!rcvRq->m_buf)
                {
                    perror("error on allocating buffer");
                }
            }
            retVal = InternalRecv(_client, rcvRq->m_buf);
            if(retVal)
            {
                break;
            }

            if(rcvRq->m_bytesDone == rcvRq->m_size)                     /*  If all data arrived                         */
            {
                (*_server->m_newMssgFunc)(_server->m_application, _client, rcvRq->m_buf, rcvRq->m_size);       /*  call the call back function for new message */
                rcvRq->m_recvState = READ_HEADER;                       /*  now ready for new header                    */
                rcvRq->m_bytesDone = 0;
                rcvRq->m_size = _server->m_headerSize;
                free(rcvRq->m_buf);
                rcvRq->m_buf = 0;                                       /*  indicate that buffer not allocated          */
            }
        }
    }

    return retVal;
}


#ifdef EXCEP
int Excep(GenServer* _server, ClientData* _client)
{
    int        retVal  = 0 ;
    IORequest*  rcvRq  = 0 ;

    assert(_server);
    assert(_client);

    rcvRq = &(_client->m_excpRq);

    while(1)
    {
            rcvRq->m_size = 1;
            if(rcvRq->m_bytesDone == 0 && !rcvRq->m_buf)    /*  If buffer no allocated  */
            {
                rcvRq->m_buf = malloc(1);
                if(!rcvRq->m_buf)
                {
                    perror("error on reallocating buffer");
                }
            }
            retVal = InternalRecv(_server, _client, rcvRq->m_buf , 1,  &(rcvRq->m_bytesDone), MSG_OOB);
            if(retVal)
            {
                break;
            }

            if(rcvRq->m_bytesDone == rcvRq->m_size)                     /*  If all data arrived   */
            {
                (*_server->m_newExcpFunc)(_server->m_application, _client->m_fd, rcvRq->m_buf, rcvRq->m_size + _server->m_headerSize);       /*  call the call back function for new message */

                rcvRq->m_bytesDone = 0;
                free(rcvRq->m_buf);
                rcvRq->m_buf = 0;                                       /*  indicate that buffer not allocated          */
            }
        }

    return retVal;

}
#endif  /*  EXCEP   */


static int Send(GenServer* _server, ClientData* _client)
{
    ssize_t     bytesSnt        ;
    IORequest*  sendRq      = 0 ;

    assert(_server);
    assert(_client);

    while(!ListIsEmpty(_client->m_sendList))                                    /*  Check there is write missions in list   */
    {
        sendRq = (IORequest*)ListHead(_client->m_sendList);                     /*  First in Queue                          */

        bytesSnt = send(_client->m_fd, sendRq->m_buf + sendRq->m_bytesDone, sendRq->m_size - sendRq->m_bytesDone, MSG_DONTWAIT);
        if(bytesSnt == -1)
        {
            if(errno == ENOTCONN || errno == ECONNRESET)                           /*  The connection shutdown                                     */
            {
                perror("connection shut down");
                _client->m_drop = DS_SELF_DROPPED;
                return 1;
            }
            if(errno == EINTR)                              /*  A signal interrupted => try again                           */
            {
                continue;
            }
            if(errno == EWOULDBLOCK)                        /*  There isn't prepared data to read                           */
            {
                return 1;
            }
            else
            {
                perror("send");
                return -1;                                  /*  critical error arrived, break loop and return -1.           */
            }
        }

        sendRq->m_bytesDone += bytesSnt;
        if(sendRq->m_bytesDone == sendRq->m_size)           /*  If finished to send                                         */
        {
            free(sendRq->m_buf);                            /*  free buffer                                                 */
            ListPopH(_client->m_sendList);                  /*  pop the request out of list                                 */
            free(sendRq);                                   /*  free IORequest                                              */
        }
    }

    return 0;
}


static size_t CalcAndFreeNonWritten(ClientData* _client)
{
    size_t totalBytes = 0;
    IORequest* sendRq = 0;

    assert(_client);

    while(!ListIsEmpty(_client->m_sendList))
    {
        sendRq = (IORequest*)ListHead(_client->m_sendList);
        totalBytes += sendRq->m_size - sendRq->m_bytesDone;
        free(sendRq->m_buf);
        ListPopH(_client->m_sendList);
        free(sendRq);
    }

    return totalBytes;
}

/**************************************************************************************************************************************/
/*********************************************          Interface Functions         ***************************************************/
/**************************************************************************************************************************************/

GenServer* GenServerNew(int _port, void* _app, size_t _headerSize, HeaderCalcFunc _headCalcFun, NewClientFunc _newClFun,
                        NewMssgFunc _newMssgFun,  DisconnectFunc _disconnFun)
{
    GenServer* server = 0;

    assert(_app && _headCalcFun && _newClFun && _newMssgFun  && _disconnFun);

    server = (GenServer*)malloc(sizeof(GenServer));
    if(!server)
    {
        perror("error in allocating server");
        return 0;
    }

    server->m_port            = _port                 ;
    server->m_serverStop      = 0                     ;
    server->m_application     = _app                  ;
    server->m_headerSize      = _headerSize           ;
    server->m_headCalcFunc    =  _headCalcFun         ;
    server->m_newClientFunc   = _newClFun             ;
    server->m_newMssgFunc     = _newMssgFun           ;
    server->m_disconnectFunc  = _disconnFun           ;
    server->m_clientsTree     = TreeNew(LessClientFd) ;
    if(!server->m_clientsTree)
    {
        perror("error in allocating server's clients container");
        free(server);
        return 0;
    }
    server->m_udpClientsTree = TreeNew(LessUDPClient);
    if(!server->m_udpClientsTree)
    {
        perror("error in allocating server's clients container");
        TreeDel(server->m_udpClientsTree);
        free(server);
        return 0;
    }

    server->m_scheduler = SchedCreate(PQ_SIZE);
    if(!server->m_scheduler)
    {
        perror("error in allocating scheduler");
        TreeDel(server->m_clientsTree);
        TreeDel(server->m_udpClientsTree);
        free(server);
        return 0;
    }

    return server;
}


int CheckTcpSets(GenServer* _server, fd_set* _readFds, fd_set* _writeFds, fd_set* _excpFds, int* _setFds)
{
    TreeIter    it                  ;
    ClientData* client  = 0         ;
    ErrGSRun    retVal  = 0         ;

    if(FD_ISSET(_server->m_tcpFd, _readFds))
    {
        retVal = Accept(_server);       /*  Acceptthe new connection        */
        if(retVal)                      /*  if error occured in acception   */
        {
            return retVal;
        }
        --*_setFds;
    }

    for(it = TreeBegin(_server->m_clientsTree); *_setFds && !TreeIsEqual(it, TreeEnd(_server->m_clientsTree)); it = TreeNext(it))
    {
        client = (ClientData*)TreeGetData(it);
        if(FD_ISSET(client->m_fd, _readFds))
        {
            if(Recv(_server, client) == -1)
            {
                return GSERR_RECV;
            }
            --*_setFds;
        }

        if(FD_ISSET(client->m_fd, _writeFds))
        {
            if(Send(_server, client) == -1)
            {
                return GSERR_SEND;
            }
            --*_setFds;
        }

        /*if(FD_ISSET(client->m_fd, &excpFds))
        {
            Excep(_server, client);
            --setFds;
        }*/
    }

    return 0;
}

int GenServerRun(GenServer* _server)
{
    fd_set          readFds         ;
    fd_set          writeFds        ;
    fd_set          excpFds         ;
    int             maxFd           ;
    int             setFds          ;
    ErrGSRun        retVal  = 0     ;
    struct timeval  tv              ;

    assert(_server);
    assert(_server->m_application);

    retVal = Init(_server);             /*  Init the server */
    if(retVal)
    {
        fprintf(stderr, "error in init server connection\n");
        return retVal;
    }

    while(!_server->m_serverStop)                                   /*  Select loop while not marked to stop        */
    {
        maxFd = InitFdSets(_server, &readFds, &writeFds, &excpFds); /*  init the fdSets, return the max fd in sets  */
        tv = SchedGetTimeOut(_server->m_scheduler);
        if(tv.tv_sec == -1)
        {
            setFds = select(maxFd + 1,          /*  one more than the maximum of any file descriptor in any of the sets                         */
                            &readFds,           /*  set is watched to see if data is available for reading from any of its file descriptors.    */
                            &writeFds,          /*  set  is watched to see if there is space to write data to any of its file descriptors.      */
                            &excpFds,           /*  set is watched for "exceptional conditions".                                                */
                            0);               /*  timeout of struct timeval, time select() may wait before returning                          */
        }
        else
        {
            setFds = select(maxFd + 1,          /*  one more than the maximum of any file descriptor in any of the sets                         */
                            &readFds,           /*  set is watched to see if data is available for reading from any of its file descriptors.    */
                            &writeFds,          /*  set  is watched to see if there is space to write data to any of its file descriptors.      */
                            &excpFds,           /*  set is watched for "exceptional conditions".                                                */
                            &tv);               /*  timeout of struct timeval, time select() may wait before returning                          */
        }
        if(setFds == -1)
        {
            perror("select");
            return GSERR_SELECT;
        }
        if(!setFds)                         /*  If the select loop returnd because of timeout expired                                       */
        {
            SchedRunOnce(_server->m_scheduler);
            continue;
        }

        if(FD_ISSET(0, &readFds))           /*  If 'enter' is clicked by user stop the server   */
        {
            _server->m_serverStop = 1;
            --setFds;
            fprintf(stderr, "Server stopped by user's keyboard\n");
        }

        /*if(FD_ISSET(_server->m_udpFd, &readFds))
        {
            UdpRecv();
        }*/

        retVal = CheckTcpSets(_server, &readFds, &writeFds, &excpFds, &setFds);
        if(retVal)
        {
            return retVal;
        }
    }

    return 0;
}


void GenServerStop(GenServer* _server)
{
    assert(_server);
    assert(_server->m_application);

    _server->m_serverStop = 1;          /*  Set the stop flag to 1  */
}


int GenServerDrop(GenServer* _server, GSKey _key)
{
    ClientData* clientPtr = 0   ;

    assert(_server);
    assert(_server->m_application);
    assert(_key);

    clientPtr = (ClientData*)_key;
    clientPtr->m_drop = DS_SERVER_DROPPED;                      /*  Set to dropped by server                        */

    return 0;
}


int GenServerWrite(GenServer* _server, GSKey _key, void* _buffer, size_t _size)
{
    ClientData* clientPtr = 0   ;
    IORequest*  ioReq     = 0   ;

    assert(_server);
    assert(_server->m_application);
    assert(_buffer);
    assert(_size);
    assert(_key);

    clientPtr = (ClientData*)_key;

    ioReq = (IORequest*)malloc(sizeof(IORequest));          /*  Create new IORequest struct for send    */
    if(!ioReq)
    {
        perror("error on allocation write request");
        return GSERR_ALLOCATION;
    }
    ioReq->m_buf = malloc(sizeof(char) * _size);
    if(!ioReq->m_buf)
    {
        perror("error on allocation buffer");
        free(ioReq);
        return GSERR_ALLOCATION;
    }
    memcpy(ioReq->m_buf, _buffer, _size);
    ioReq->m_bytesDone  = 0     ;
    ioReq->m_size       = _size ;
    if(ListPushT(clientPtr->m_sendList, ioReq))             /*  push the new ioRequest to send list     */
    {
        fprintf(stderr,"error on insert to send list\n");
        free(ioReq->m_buf);
        free(ioReq);
        return GSERR_INSERT_LIST;
    }

    return 0;
}


void GenServerDel(GenServer* _server)
{
    TreeIter it;

    assert(_server);
    assert(_server->m_application);

    for(it = TreeBegin(_server->m_clientsTree); TreeIsEqual(it, TreeEnd(_server->m_clientsTree)); it = TreeNext(it))
    {
        RemoveClient(_server, (ClientData*)TreeGetData(it));
    }

    TreeDel(_server->m_clientsTree);
    free(_server);
}


int GenServerSchedAddTask(GenServer* _server, int(*_func)(void* _data), size_t _period, void* _data)
{
    assert(_server);
    assert(_func);

    return SchedRunEvery(_server->m_scheduler, _func, _period, _data);
}
